package com.wutianyi.datastore;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.wutianyi.datastore.DataStoreProtos.BProperty;
import com.wutianyi.datastore.DataStoreProtos.BytesProperty;
import com.wutianyi.datastore.DataStoreProtos.DProperty;
import com.wutianyi.datastore.DataStoreProtos.FProperty;
import com.wutianyi.datastore.DataStoreProtos.IProperty;
import com.wutianyi.datastore.DataStoreProtos.LProperty;
import com.wutianyi.datastore.DataStoreProtos.Properties;
import com.wutianyi.datastore.DataStoreProtos.SProperty;

@SuppressWarnings("rawtypes")
public abstract class AbstractDataStoreServicesImpl implements DataStoreServices
{
    private static Logger logger = LoggerFactory.getLogger(AbstractDataStoreServicesImpl.class);

    private static Set<Class> validClass;
    static
    {
        validClass = new HashSet<Class>();
        validClass.add(int.class);
        validClass.add(long.class);
        validClass.add(String.class);
        validClass.add(byte[].class);
        validClass.add(boolean.class);
        validClass.add(double.class);
        validClass.add(float.class);

        validClass.add(Integer.class);
        validClass.add(Long.class);
        validClass.add(Double.class);
        validClass.add(Boolean.class);
        validClass.add(Float.class);
    }

    @Override
    public void putEntity(Entity entity)
    {

        Properties.Builder builder = DataStoreProtos.Properties.newBuilder();

        for (Entry<String, Object> entry : entity.getProperties().entrySet())
        {
            Object v = entry.getValue();
            Class vc = v.getClass();
            if (!validClass.contains(vc))
            {
                throw new IllegalArgumentException("Entity Value can not support this type: '"
                        + v.getClass().getCanonicalName() + "'");
            }
            if (vc.equals(int.class) || vc.equals(Integer.class))
            {
                builder.addIProperties(IProperty.newBuilder().setProperty(entry.getKey()).setValue((Integer) v).build());
            }
            else if (vc.equals(long.class) || vc.equals(Long.class))
            {
                builder.addLProperties(LProperty.newBuilder().setProperty(entry.getKey()).setValue((Long) v).build());
            }
            else if (vc.equals(double.class) || vc.equals(Double.class))
            {
                builder.addDProperties(DProperty.newBuilder().setProperty(entry.getKey()).setValue((Double) v).build());
            }
            else if (vc.equals(float.class) || vc.equals(Float.class))
            {
                builder.addFProperties(FProperty.newBuilder().setProperty(entry.getKey()).setValue((Float) v).build());
            }
            else if (vc.equals(boolean.class) || vc.equals(Boolean.class))
            {
                builder.addBProperties(BProperty.newBuilder().setProperty(entry.getKey()).setValue((Boolean) v).build());
            }
            else if (vc.equals(byte[].class))
            {
                byte[] bv = (byte[]) v;
                builder.addBytesProperties(BytesProperty.newBuilder().setProperty(entry.getKey())
                        .setValue(ByteString.copyFrom(bv)).build());
            }
            else
            {
                builder.addSProperties(SProperty.newBuilder().setProperty(entry.getKey()).setVlaue((String) v).build());
            }
        }
        Properties ps = builder.build();
        storeEntity(entity, ps);
    }

    protected abstract void storeEntity(Entity entity, Properties properties);

    protected Entity parseByteToEntity(String kind, String key, byte[] bytes, Set<String> indexes)
    {
        try
        {
            Entity entity = new Entity(kind, key);
            Properties properties = Properties.parseFrom(bytes);
            Map<String, Object> originalIndexProperties = new HashMap<String, Object>();

            for (IProperty iProperty : properties.getIPropertiesList())
            {
                String p = iProperty.getProperty();
                boolean isIndex = false;
                if (indexes.contains(p))
                {
                    originalIndexProperties.put(p, iProperty.getValue());
                    isIndex = true;
                }
                entity.setProperty(p, iProperty.getValue(), isIndex);

            }

            for (SProperty sProperty : properties.getSPropertiesList())
            {
                String p = sProperty.getProperty();
                boolean isIndex = false;
                if (indexes.contains(p))
                {
                    originalIndexProperties.put(p, sProperty.getVlaue());
                    isIndex = true;
                }
                entity.setProperty(p, sProperty.getVlaue(), isIndex);
            }

            for (DProperty dProperty : properties.getDPropertiesList())
            {
                String p = dProperty.getProperty();
                boolean isIndex = false;
                if (indexes.contains(p))
                {
                    originalIndexProperties.put(p, dProperty.getValue());
                    isIndex = true;
                }
                entity.setProperty(p, dProperty.getValue(), isIndex);
            }
            for (FProperty fProperty : properties.getFPropertiesList())
            {
                String p = fProperty.getProperty();
                boolean isIndex = false;
                if (indexes.contains(p))
                {
                    originalIndexProperties.put(p, fProperty.getValue());
                    isIndex = true;
                }
                entity.setProperty(p, fProperty.getValue(), isIndex);
            }

            for (LProperty lProperty : properties.getLPropertiesList())
            {
                String p = lProperty.getProperty();
                boolean isIndex = false;
                if (indexes.contains(p))
                {
                    originalIndexProperties.put(p, lProperty.getValue());
                    isIndex = true;
                }
                entity.setProperty(p, lProperty.getValue(), isIndex);
            }

            for (BProperty bProperty : properties.getBPropertiesList())
            {
                String p = bProperty.getProperty();
                boolean isIndex = false;
                if (indexes.contains(p))
                {
                    originalIndexProperties.put(p, bProperty.getProperty());
                    isIndex = true;
                }
                entity.setProperty(p, bProperty.getValue(), isIndex);
            }

            for (BytesProperty bytesProperty : properties.getBytesPropertiesList())
            {
                entity.setProperty(bytesProperty.getProperty(), bytesProperty.getValue());
            }
            entity.setOriginalIndexProperties(originalIndexProperties);
            return entity;
        }
        catch (InvalidProtocolBufferException e)
        {
            logger.error("", e);
        }
        return null;
    }

    public static void main(String[] args)
    {
        Object i = new Integer(10);
        System.out.println(Integer.TYPE.isPrimitive());
        System.out.println(i.getClass().isMemberClass());
        System.out.println(validClass.contains(int.class));
        System.out.println(Integer.class.getCanonicalName());
    }
}
